/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		acleditor editpopup.cpp
 * PURPOSE:		acleditor editbox popup creation/callback state functions
 *				included as part of acleditor.cpp
 */

void __stdcall SidEnumDisplayFunc(PCWSTR Name, SID const &Data, PVOID Context, MenuEnum::DisplayName<SID>::ReturnFunc Return)
{
	auto_szpod<WCHAR> SidText = SidToText(&Data);

	Return(Context, SidText);
}

class AclEditorEditPopup : public AclEditorLookup
{
	auto_ptr<MenuEnum::MenuBase<SID> > TrusteeNameMenuEnum;
	auto_ptr<MenuEnum::MenuBase<SID> > TrusteeSidMenuEnum;
	auto_ptr<MenuEnum::MenuBase<GUID> > GuidTypeMenuEnum;
	auto_ptr<MenuEnum::MenuBase<GUID> > GuidInhTypeMenuEnum;

protected:
	AclEditorEditPopup(AclEditorInitParams const &Init) :
		AclEditorLookup(Init),
		TrusteeNameMenuEnum(auto_take_(Init.Enumerators->SidEnumerator ? new MenuEnum::MenuBase<SID>(Init.Enumerators->SidEnumerator, NULL, Init.Prefs.MenuBreakFactor) : NULL)),
		TrusteeSidMenuEnum(auto_take_(Init.Enumerators->SidEnumerator ? new MenuEnum::MenuBase<SID>(Init.Enumerators->SidEnumerator, &SidEnumDisplayFunc, Init.Prefs.MenuBreakFactor) : NULL)),
		GuidTypeMenuEnum(auto_take_(Init.Enumerators->GuidTypeEnumerator ? new MenuEnum::MenuBase<GUID>(Init.Enumerators->GuidTypeEnumerator, NULL, Init.Prefs.MenuBreakFactor) : NULL)),
		GuidInhTypeMenuEnum(auto_take_(Init.Enumerators->GuidInhTypeEnumerator ? new MenuEnum::MenuBase<GUID>(Init.Enumerators->GuidInhTypeEnumerator, NULL, Init.Prefs.MenuBreakFactor) : NULL))
	{}

private:
	void ReturnRawNameEditTextGuarded(ULONG RowIndex, PCWSTR Text)
	{
		SetListBoxItemText(Columns[COL_TSTE_NAME].Wnd, RowIndex, L"<Looking up name>");

		auto_szpod<SID> Sid = TextToSid(Text);

		LookupSid(Rows[RowIndex], Sid);
	}

	void ReturnGuidText(ULONG RowIndex, UCHAR Col, PCWSTR Text)
	{
		if(Text[0] == NULL) //null string == no guid
		{
			if(Col == COL_OBJ_TYPE)
				Rows[RowIndex]->TypeGuid.reset();
			else//Col == COL_OBJ_INH_TYPE
				Rows[RowIndex]->InhTypeGuid.reset();
			return;
		}

		auto_szpod<GUID> Guid = TextToGuid(Text);

		if(Guid) //if Text was in guid format
		{
			if(Col == COL_OBJ_TYPE)
				Rows[RowIndex]->TypeGuid.take(Guid);
			else//Col == COL_OBJ_INH_TYPE
				Rows[RowIndex]->InhTypeGuid.take(Guid);

			if(Col == COL_OBJ_TYPE)
				LookupObjectTypeGuid(Rows[RowIndex], *Guid, Col);
			else
				LookupObjectInhTypeGuid(Rows[RowIndex], *Guid, Col);
		}
		else //lookup the name
		{
			if(Col == COL_OBJ_TYPE)
				LookupObjectTypeName(Rows[RowIndex], Text, Col);
			else
				LookupObjectInhTypeName(Rows[RowIndex], Text, Col);
		}
	}

	void ReturnEditText(UCHAR Col, DIALOG_NOTIFY_OPERATION Operation, ULONG_PTR Data)
	{
		if(Operation != DN_CLOSE_OK)
			return;

		PCWSTR Text = reinterpret_cast<PCWSTR>(Data);
		USHORT RowIndex = GetSelectedRowIndex(Col);
	
		SetListBoxItemText(Columns[Col].Wnd, RowIndex, Text);

		switch(Col)
		{
		case COL_TSTE_NAME:
			__try
			{
				Rows[RowIndex]->SidUpdateIndex++;
				SetListBoxItemText(Columns[COL_TSTE_SID].Wnd, RowIndex, L"<Looking up SID>");
				LookupName(Rows[RowIndex], Text);
			}
			__except(HandleLookupException(exception_info(), COL_TSTE_SID, RowIndex))
			{}
			break;

		case COL_TSTE_SID:
			__try
			{
				Rows[RowIndex]->SidUpdateIndex++;
				ReturnRawNameEditTextGuarded(RowIndex, Text);
			}
			__except(HandleLookupException(exception_info(), COL_TSTE_NAME, RowIndex))
			{}
			break;

		case COL_OBJ_TYPE:
		case COL_OBJ_INH_TYPE:
			__try
			{
				if(Col == COL_OBJ_TYPE)
					Rows[RowIndex]->TypeUpdateIndex++;
				else //Col == COL_OBJ_INH_TYPE
					Rows[RowIndex]->InhTypeUpdateIndex++;

				ReturnGuidText(RowIndex, Col, Text);
			}
			__except(HandleLookupException(exception_info(), Col, RowIndex))
			{}
		}
	}

	struct EditPopupContext
	{
		AclEditorEditPopup *Wo;
		UCHAR Col;

		EditPopupContext(AclEditorEditPopup *Wo, UCHAR Col) :
			Wo(Wo),
			Col(Col)
		{}

		static ULONG_PTR CALLBACK RecieveNotify(PVOID Context, DIALOG_NOTIFY_OPERATION Operation, ULONG_PTR Data)
		{
			EditPopupContext *epc = reinterpret_cast<EditPopupContext *>(Context);

			epc->Wo->ReturnEditText(epc->Col, Operation, Data);

			if(Operation <= DN_CLOSE_MAX) //a close message is always the last one
				delete epc;

			return 0;
		}
	};

	void NewEditPopup(HWND ListWnd, ULONG RowIndex, PWSTR InitialText, HWND Parent, UCHAR Col)
	{
		RECT rect = GetListBoxItemScreenRect(ListWnd, RowIndex);

		auto_refct<EditPopup::WindowObject> Popup = new EditPopup::WindowObject(InitialText, Parent, Col == COL_TSTE_SID ? ES_UPPERCASE : 0, Prefs.Font, &EditPopupContext::RecieveNotify, new EditPopupContext(this, Col), rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
	}

	template <class T> void ReturnMenuText(UCHAR Col, DIALOG_NOTIFY_OPERATION Operation, MenuEnum::ReturnData<T> const *Data);

	void ReturnMenuSidTextSEH(UCHAR Col, USHORT RowIndex, MenuEnum::ReturnData<SID> const *Data)
	{
		__try
		{
			Rows[RowIndex]->SidUpdateIndex++;
			SetListBoxItemText(Columns[COL_TSTE_NAME].Wnd, RowIndex, Data->Name);
			LookupSid(Rows[RowIndex], Data->Data);
		}
		__except(HandleLookupException(exception_info(), Col, RowIndex))
		{}
	}

	template <> void ReturnMenuText<SID>(UCHAR Col, DIALOG_NOTIFY_OPERATION Operation, MenuEnum::ReturnData<SID> const *Data)
	{
		USHORT RowIndex = GetSelectedRowIndex(Col);
		auto_szpod<WCHAR> SidText = SidToText(Data->Data);
	
		SetListBoxItemText(Columns[COL_TSTE_SID].Wnd, RowIndex, SidText);

		ReturnMenuSidTextSEH(Col, RowIndex, Data);
	}

	template <> void ReturnMenuText<GUID>(UCHAR Col, DIALOG_NOTIFY_OPERATION Operation, MenuEnum::ReturnData<GUID> const *Data)
	{
		USHORT RowIndex = GetSelectedRowIndex(Col);
	
		SetListBoxItemText(Columns[Col].Wnd, RowIndex, Data->Name);

		switch(Col)
		{
		case COL_OBJ_TYPE:
			Rows[RowIndex]->TypeUpdateIndex++;
			Rows[RowIndex]->TypeGuid = Data->Data;
			break;

		case COL_OBJ_INH_TYPE:
			Rows[RowIndex]->InhTypeUpdateIndex++;
			Rows[RowIndex]->InhTypeGuid = Data->Data;
			break;
		}
	}

	struct MenuPopupContext
	{
		AclEditorEditPopup *Wo;
		UCHAR Col;

		MenuPopupContext(AclEditorEditPopup *Wo, UCHAR Col) :
			Wo(Wo),
			Col(Col)
		{}

		static ULONG_PTR CALLBACK RecieveNotify(PVOID Context, DIALOG_NOTIFY_OPERATION Operation, ULONG_PTR Data)
		{
			MenuPopupContext *epc = reinterpret_cast<MenuPopupContext *>(Context);

			if(Operation == DN_CLOSE_OK)
				switch(epc->Col)
				{
				case COL_TSTE_NAME:
				case COL_TSTE_SID:
					epc->Wo->ReturnMenuText(epc->Col, Operation, reinterpret_cast<MenuEnum::ReturnData<SID> *>(Data));
					break;

				case COL_OBJ_TYPE:
				case COL_OBJ_INH_TYPE:
					epc->Wo->ReturnMenuText(epc->Col, Operation, reinterpret_cast<MenuEnum::ReturnData<GUID> *>(Data));
					break;
				}

			if(Operation <= DN_CLOSE_MAX) //a close message is always the last one
				delete epc;

			return 0;
		}
	};

	template <class T> void ShowEnumMenuForColumn2(UCHAR Col, auto_ptr<MenuEnum::MenuBase<T> > &Menu, int x, int y)
	{
		if(!Menu) //no enum for this column supported
			return;

		if(RowMax == MAXUSHORT)
			return;

		HWND SrcWnd = Columns[Col].Wnd;

		//simulate a click to ensure the correct item is selected, even if command originated from the keyboard
		POINT pt;
		pt.x = x;
		pt.y = y;

		ScreenToClient(SrcWnd, &pt);

		SendMessage(SrcWnd, WM_LBUTTONDOWN, 0, MAKELPARAM(pt.x, pt.y));
		SendMessage(SrcWnd, WM_LBUTTONUP, 0, MAKELPARAM(pt.x, pt.y));

		DialogCallback Dc(&MenuPopupContext::RecieveNotify, new MenuPopupContext(this, Col));

		Menu->ShowMenu(Wnd, Dc, x, y);
	}

protected:
	void ShowTextEditorForColumn(UCHAR Col)
	{
		int RowIndex = GetSelectedRowIndex(Col);

		if(RowIndex == MAXUSHORT)
			return;

		auto_szpod<WCHAR> Text = GetNewListBoxText(Columns[Col].Wnd, RowIndex);

		NewEditPopup(Columns[Col].Wnd, RowIndex, Text, Wnd, Col);
	}

	void ShowEnumMenuForColumn(UCHAR Col, int x, int y)
	{
		switch(Col)
		{
		case COL_TSTE_NAME:
			ShowEnumMenuForColumn2(Col, TrusteeNameMenuEnum, x, y);
			break;

		case COL_TSTE_SID:
			ShowEnumMenuForColumn2(Col, TrusteeSidMenuEnum, x, y);
			break;

		case COL_OBJ_TYPE:
			ShowEnumMenuForColumn2(Col, GuidTypeMenuEnum, x, y);
			break;

		case COL_OBJ_INH_TYPE:
			ShowEnumMenuForColumn2(Col, GuidInhTypeMenuEnum, x, y);
			break;
		}
	}

	virtual LRESULT RecieveMessage(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) override
	{
		switch(msg)
		{
		case WM_INITMENUPOPUP:
			if(TrusteeNameMenuEnum)
				TrusteeNameMenuEnum->FillSubMenu(reinterpret_cast<HMENU>(wparam));
			if(TrusteeSidMenuEnum)
				TrusteeSidMenuEnum->FillSubMenu(reinterpret_cast<HMENU>(wparam));
			if(GuidTypeMenuEnum)
				GuidTypeMenuEnum->FillSubMenu(reinterpret_cast<HMENU>(wparam));
			if(GuidInhTypeMenuEnum)
				GuidInhTypeMenuEnum->FillSubMenu(reinterpret_cast<HMENU>(wparam));
			break;
		}

		return __super::RecieveMessage(wnd, msg, wparam, lparam);
	}
};